The COVID-19 outbreak is an unprecedented global public health challenge. It is a heated topic in social networks. People discuss it and share the news about it as the number of new cases are soaring. In order to analyse how people discuss it in Twiiter and what’s people’s response, I will use text analysis tools in this article.
My mission is to aquire the latest COVID-19 tweets data, clean the data for analysis with data transformation, then do modelling by text analysis methods such as Bag of Words, Sentiment Analysis, Association Rules and Topic Modelling.
Let’s start with extracting data from Twitter.
1. Data Obtaining
# load libraries
library(tidytext)
library(tm)
library(tidyverse)
library(textclean)
library(ggplot2)
library(ggthemes)
library(reshape2)
library(wordcloud)
library(topicmodels)
library(textdata)
library(rtweet)
# Extract data by function of rtweet
rt <- search_tweets(
# Set search keyword
q = "#COVID19",
# Set the number of tweets to return
n = 18000,
# Specify the type of search results
type = "mixed",
# Do not include retweets
include_rts = FALSE,
# Set tweet language to English
lang = "en",
# Twitter rate limits cap the number of search results returned to 18,000 every 15 minutes. To request more than that, simply set retryonratelimit = TRUE and rtweet will wait for rate limit resets.
retryonratelimit = TRUE
)
# save the returned data to csv
save_as_csv(rt, "covid19.csv")
After saving the tweets data, we can load it directly.
rt <- read.csv(file = "covid19.csv", stringsAsFactors = FALSE)
# check the dimension of returned tweets data
dim(rt)
[1] 17947 90
There are 17947 tweets returned to our dataframe with 90 variables.
# View the dataframe
View(rt)
Because we specified the search results by "mixed", so let’s order the tweets by post time and check the first and last 10 tweets.
# check the first 10 rows by ordering created_at time
head(rt[order(rt$created_at),], 10)
# check the last 10 rows by ordering created_at time
tail(rt[order(rt$created_at),], 10)
Only 3 tweets were posted at least 10 hours than others. So we can plot the number of tweets created by time to see the trend.
# Plot the frequency of tweets after 2020-03-28 23:00:00 with interval 1 min
ts_plot(rt %>% filter(created_at > "2020-03-28 23:00:00"),
by = "mins",
trim = 1,
color = "#00acee"
) +
ggplot2::theme_minimal() +
ggplot2::theme(plot.title = ggplot2::element_text(face = "bold")) +
ggplot2::labs(
x = NULL, y = NULL,
title = "Frequency of #COVID19 tweets from past hours",
subtitle = "Twitters counts aggregated using 1 mins intervals",
caption = "\nSource: Data collected from Twitter's REST API via rtweet"
)

We can see that the number of tweets created per minutes is in 50 to 150 range. At every o’clock and half past o’clock, the number of tweets published is much higher than other timing.
3. Data Cleaning
Now text cleaning is to be conducted, as for text analysis it is necessary to remove punctuation, numbers, white space, stop words and specified words such as “virus”, “covid”, “coronavirus”. Therefor, we can create a function to clean our text data.
clean_corpus <- function(corp) {
# remove any html format if any
corp <- tm_map(corp, content_transformer(replace_html))
# remove any URLs
corp <- tm_map(corp, content_transformer(replace_url))
# transform all characters to lower case
corp <- tm_map(corp, content_transformer(tolower))
# word stemming
corp <- tm_map(corp, stemDocument)
# remove punctuation
corp <- tm_map(corp, removePunctuation)
# remove numbers
corp <- tm_map(corp, removeNumbers)
# strip extra whitespace
corp <- tm_map(corp, stripWhitespace)
# remove stop words and "apple"
# Remove english common stopwords
corp <- tm_map(corp, removeWords, stopwords("english"))
corp <- tm_map(corp, removeWords,
c("covid19", "coronavirus", "covid", "will", "can", "now", "get", "just",
"new", "peopl", "one", "virus", "case", "day", "time", "pendem", "like",
"take", "make", "say", "see"))
# word stemming
#corp <- tm_map(corp, stemDocument)
# remove special characters
#f <- content_transformer(function(x, pattern) gsub(pattern, "", x))
#corp <- tm_map(corp, f, "\”“")
# [\[\]"',.^&$%#]
return(corp)
}
cv_corpus_cleaned <- clean_corpus(cv_corpus)
# print the first tweet after cleaning
cat("Before:\n", content(cv_corpus[[1]]), "\n\n")
Before:
11 new cases of #COVID19 have been reported in Nigeria: 8 in Lagos, 2 in Enugu & 1 in Edo State
As at 11:55pm 27th March, there are 81 confirmed cases of #COVID19 reported in Nigeria. 3 have been discharged with 1 death. https://t.co/7p3v3qAcGM
cat("After:\n", content(cv_corpus_cleaned[[1]]))
After:
report nigeria lagos enugu edo state pm th march confirm report nigeria discharg death
Our function successfully changed all letters to lowercase, removed the numbers, url, punctuation, stop words and the specified words. In addition, the stemming was conducted for words like “reported”, “confirmed”, “discharged”. Now we can start our modelling work.
4. Modelling
In modelling part, we will apply four methods to understand the COVID-19 tweets data. 1. Bag of Words 2. Gauge Sentiment 3. Association Rules 4. Topic Modelling
4.1 Bag of Words Model
# transform the cleaned data into a doc-term matrix
dt_cv_corpus <- DocumentTermMatrix(cv_corpus_cleaned)
# print the dimension of the matrix
dim(dt_cv_corpus)
[1] 17947 38962
We have 17951 tweets and 38962 different words/terms.
# transform to matrix format
mt_cv_corpus <- as.matrix(dt_cv_corpus)
# view a portion of the matrix
mt_cv_corpus[1:10, 50:55]
Terms
Docs ‘em ‘emerg ‘essenti ‘essential’ ‘everi ‘exit
1 0 0 0 0 0 0
2 0 0 0 0 0 0
3 0 0 0 0 0 0
4 0 0 0 0 0 0
5 0 0 0 0 0 0
6 0 0 0 0 0 0
7 0 0 0 0 0 0
8 0 0 0 0 0 0
9 0 0 0 0 0 0
10 0 0 0 0 0 0
# visualise twitter data
# convert from matrix to tidy data
tidy_data <- tidy(dt_cv_corpus)
tidy_data
term_fre <- colSums(mt_cv_corpus)
# sort term frequency in decreasing order
term_fre <- sort(term_fre, decreasing = TRUE)
freq_word <- data.frame(word = names(term_fre), freq = term_fre)
head(freq_word, 20)
# plot the most frequent 20 terms in our data
ggplot(head(freq_word, 20), aes(x = reorder(word, freq), y = freq), fill = "blue") +
geom_col(fill = "#00acee") +
ggplot2::theme_minimal() +
ggplot2::theme(plot.title = ggplot2::element_text(face = "bold")) +
ggplot2::theme(axis.text.x = ggplot2::element_blank()) +
ggplot2::labs(
x = NULL, y = NULL,
title = "Most frequent 20 words in #COVID19 tweets\n",
caption = "\nSource: Data collected from Twitter's REST API via rtweet"
) +
coord_flip() + geom_text(aes(label = freq), hjust = +1.2, color = "#FFFFFF", size = 3)

Having the frequency of each terms, let’s make a wordcloud to visualise the most frequent terms.
# create a vector of term frequency values
terms_vec <- names(term_fre)
# create wordcloud for 60 most frequent words
#library(viridisLite)
#color_pal <- plasma(n = 5)
wordcloud(words = terms_vec,
freq = term_fre,
min.freq = 300,
max.words = 80,
scale = c(3,0.13),
random.order = FALSE, # more frequent words will be in center
rot.per = 0.0, # proportion words with 90 degree rotation
colors = brewer.pal(6, "Set2")) # Set color theme with 5 colors

The wordloud show the most frequent words in the middle. We can see words such as need, help, dure, test, death, stay, home. These words are definitely associated with coronavirus as we know the situation around the world. Many people need help and cure as they had taken test and increasing numbers of people were positive. Many people are durely suffering from the pandemic. The virus has lead to more than 10 thousand death. To protect ourselves, it is better to stay home and work from home.
Alternative way to visualise the wordcloud by wordcloud2 package. It creates a widget and generate the words from most frequent to less frequent.
library(wordcloud2)
wordcloud2(data = head(freq_word, 1000), fontFamily = "Helvetica", shape = "diamond")
4.2 Gauge Sentiment
Previously we got tidy_data, now we use it for sentiment analysis.
tweet_sentiment <- tidy_data %>%
inner_join(get_sentiments("bing"), by = c(term = "word"))
Because we chose bing lexicon, all terms will be categorized in a binary fashion into positive or negative.
tweet_sentiment
The result shows each term in each document has its sentiment category – positive or negative. To visualise the mose frequent positive and negative words, we should group our data by sentiment and term with sum value for count.
tweet_sentiment %>%
count(sentiment, term, wt = count) %>% # count the number of frequency of terms
filter(n > 200) %>% # only keep terms occur more than 200 times
mutate(n = ifelse(sentiment == "negative", -n, n)) %>% # change the n of negative to -n
mutate(term = reorder(term, n)) %>%
ggplot(aes(term, n, fill = sentiment)) +
geom_col() +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_blank()) +
geom_text(aes(label = n, y = n - 50*sign(n)), color = "#FFFFFF", size = 3) +
ggplot2::labs(
x = NULL, y = NULL,
title = "Most frequent words per sentiment",
caption = "\nSource: Data collected from Twitter's REST API via rtweet") +
coord_flip() # reverse x/y axis

We can make a comparison wordcloud to show the most frequent postive and negative words at the same plot.
tidy_data %>%
inner_join(get_sentiments("bing"), by = c(term = "word")) %>%
count(term, sentiment, sort = TRUE) %>%
acast(term ~ sentiment, value.var = "n", fill = 0) %>% # change molten frame to wide frame
subset(select = c(2, 1)) %>% # put positive before negative by reorder columns
comparison.cloud(max.words = 100,
rot.per = 0,
match.colors = TRUE,
random.order = FALSE
)

By comprison wordcloud, we can clearly see the two category words. Positive high frequent words include thank, protect, safe, love, support. Negative high frequent words include death, die, risk, outbreak, crisis, symptom.
The nrc lexicon can catogerize words with 10 sentiments.
# list all sentiment categories by nrc type
unique(get_sentiments("nrc")$sentiment)
[1] "trust" "fear" "negative" "sadness" "anger"
[6] "surprise" "positive" "disgust" "joy" "anticipation"
So we can make a comparison wordcloud for the other 8 sentiments excluding positive or negative.
data_tidy <- tidy_data %>%
# Inner join to nrc lexicon
inner_join(get_sentiments("nrc"), by = c("term" = "word")) %>%
# Drop positive or negative
filter(!grepl("positive|negative", sentiment)) %>%
# Count by sentiment and term
count(sentiment, term) %>%
# Spread sentiment, using n for values
spread(sentiment, n, fill = 0) %>%
# Convert to data.frame, making term the row names
data.frame(row.names = "term")
# Plot comparison cloud
comparison.cloud(data_tidy,
max.words = 100,
title.size = 1,
random.order = FALSE, # more frequent words will be in center
rot.per = 0.0, # proportion words with 90 degree rotation
colors = brewer.pal(8, "Set2") # Set color theme with 5 colors
)

Most words under each sentiment are correctly associated wit the sentiment. It’s a surprise that the word trump belong to surprise sentiment. Maybe it is not a surprise as he always give us surprise :).
4.3 Association Rules
By assoiciation rules analysis, we can find the words associated with specific word. Firstly we need to remove the terms having high sparsity (0.999).
# remove the terms having 0.999 sparsity, only terms occuring in 0.1% documents are retained
new_doc_term <- removeSparseTerms(dt_cv_corpus, 0.99)
new_doc_term
<<DocumentTermMatrix (documents: 17947, terms: 197)>>
Non-/sparse entries: 66405/3469154
Sparsity : 98%
Maximal term length: 20
Weighting : term frequency (tf)
# Convert to matrix
new_mat <- as.matrix(new_doc_term)
197 terms fulfill our filtering requirement. Now we can check the associated words. As the term need is the most frequent word, let’s see what people need most.
# Use the findAssocs function to find words associated with the 'need'
findAssocs(new_doc_term, "need", 0.03)
$need
help protect act fight keep pleas care know nurs ppe respons
0.08 0.08 0.06 0.06 0.06 0.05 0.04 0.04 0.04 0.04 0.04
save also much safe stop
0.04 0.03 0.03 0.03 0.03
It is not suprised to see that people need help, need to take care and protect themselves from the virus, need to keep safe and need ppe. For those infected, they need nurse to help.
Now we can check what terms are associated with safe, as it is important for everyone to keep safe under COVID-19 outbreak.
# Use the findAssocs function to find words associated with the 'patient'
findAssocs(new_doc_term, "safe", 0.03)
$safe
stay keep home everyon hope communiti pleas care dure
0.30 0.20 0.09 0.07 0.07 0.06 0.06 0.05 0.05
continu friend love stayhom best famili let need thank
0.04 0.04 0.04 0.04 0.03 0.03 0.03 0.03 0.03
The most associated words with safe are stay, keep and home. Actually, keeping stay home is the most efficient way to protect ourselves from the virus. Therefore, from COVID-19 tweets data, I think we should know that it is important to stay home under current serious situation.
4.5 Topic Modelling
sum_rows <- apply(new_doc_term, 1, sum)
# remove documents without words
dtm_nonzeros <- new_doc_term[sum_rows > 0, ]
# apply LDA function and set the number of topics to 4
lda <- LDA(dtm_nonzeros, k = 4)
# get the first 10 terms of each topic
term <- terms(lda, 10)
term
Topic 1 Topic 2 Topic 3 Topic 4
[1,] "death" "stay" "china" "home"
[2,] "test" "think" "need" "pandem"
[3,] "dure" "help" "pandem" "trump"
[4,] "support" "need" "live" "know"
[5,] "posit" "pleas" "stop" "respons"
[6,] "trump" "due" "famili" "help"
[7,] "lockdown" "work" "health" "work"
[8,] "fight" "test" "dure" "need"
[9,] "today" "health" "help" "live"
[10,] "die" "stayhom" "state" "good"
We can visualize the first 10 terms of each topic.
# construct a tidy data frame of the LDA model result by tidy function from tidytext package
topics <- tidy(lda, matrix = "beta")
# generate the top 10 terms of each topic
top_terms <- topics %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta)
# plot the first 10 terms by beta value
top_terms %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
ggplot2::labs(
x = NULL,
title = "Top 10 terms in each topic",
caption = "\nSource: Data collected from Twitter's REST API via rtweet") +
coord_flip() +
scale_x_reordered()

The beta value tells us probability of that term (word) belonging to that topic.
We can see some terms like help, need, trump and pandemic appear in more than 1 topic, this is not surprised. There are differences between these collections of terms. - Topic 1 is about test, die from positive, like a topic about the new cases and death report. - Topic 2 is about stay home, need and help, like a topic about what people do. - Topic 3 is more related to china. We know that recently the spreading pace of the pandemic in China is almost stopped. - In topic 4, the terms trump, know and good appear together.
LS0tCnRpdGxlOiAiVGV4dCBBbmFseXNpcyBQcm9qZWN0IC0tIENPVklELTE5IFR3ZWV0cyBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQohW10oaHR0cHM6Ly9hcGkudGltZS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDMvc2luZ2Fwb3JlX2Nvcm9uYXZpcnVzX2NvdmlkMTkuanBnP3c9ODAwJnF1YWxpdHk9ODUpCgpUaGUgQ09WSUQtMTkgb3V0YnJlYWsgaXMgYW4gdW5wcmVjZWRlbnRlZCBnbG9iYWwgcHVibGljIGhlYWx0aCBjaGFsbGVuZ2UuIEl0IGlzIGEgaGVhdGVkIHRvcGljIGluIHNvY2lhbCBuZXR3b3Jrcy4gUGVvcGxlIGRpc2N1c3MgaXQgYW5kIHNoYXJlIHRoZSBuZXdzIGFib3V0IGl0IGFzIHRoZSBudW1iZXIgb2YgbmV3IGNhc2VzIGFyZSBzb2FyaW5nLiBJbiBvcmRlciB0byBhbmFseXNlIGhvdyBwZW9wbGUgZGlzY3VzcyBpdCBpbiBUd2lpdGVyIGFuZCB3aGF0J3MgcGVvcGxlJ3MgcmVzcG9uc2UsIEkgd2lsbCB1c2UgdGV4dCBhbmFseXNpcyB0b29scyBpbiB0aGlzIGFydGljbGUuCgpNeSBtaXNzaW9uIGlzIHRvIGFxdWlyZSB0aGUgbGF0ZXN0IENPVklELTE5IHR3ZWV0cyBkYXRhLCBjbGVhbiB0aGUgZGF0YSBmb3IgYW5hbHlzaXMgd2l0aCBkYXRhIHRyYW5zZm9ybWF0aW9uLCB0aGVuIGRvIG1vZGVsbGluZyBieSB0ZXh0IGFuYWx5c2lzIG1ldGhvZHMgc3VjaCBhcyBCYWcgb2YgV29yZHMsIFNlbnRpbWVudCBBbmFseXNpcywgQXNzb2NpYXRpb24gUnVsZXMgYW5kIFRvcGljIE1vZGVsbGluZy4KCkxldCdzIHN0YXJ0IHdpdGggZXh0cmFjdGluZyBkYXRhIGZyb20gVHdpdHRlci4KCiMgMS4gRGF0YSBPYnRhaW5pbmcKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMgbG9hZCBsaWJyYXJpZXMKIyBmb3IgZGF0YSBhcXVpcmluZwpsaWJyYXJ5KHJ0d2VldCkgIyB0byByZXR1cm4gdHdlZXRzIGRhdGEKCiMgZm9yIGRhdGEgY2xlYW5pbmcKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkodGV4dGNsZWFuKQoKIyBmb3IgZGF0YSB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeSh3b3JkY2xvdWQpCgojIGZvciB0ZXh0IGFuYWx5c2lzCmxpYnJhcnkodGlkeXRleHQpICMgdGV4dCBtaW5pbmcKbGlicmFyeSh0bSkgIyB0ZXh0IG1pbmluZyBmb3IgY3JlYXRpbmcgY29ycHVzCmxpYnJhcnkodG9waWNtb2RlbHMpICMgdG9waWMgbW9kZWwgcGFja2FnZQpsaWJyYXJ5KHRleHRkYXRhKSAjIGEgZnJhbWV3b3JkIHRvIGRlYWwgd2l0aCB0ZXh0IGRhdGFzZXQKYGBgCgoKYGBge3J9CiMgRXh0cmFjdCBkYXRhIGJ5IGZ1bmN0aW9uIG9mIHJ0d2VldApydCA8LSBzZWFyY2hfdHdlZXRzKAogICMgU2V0IHNlYXJjaCBrZXl3b3JkCiAgcSA9ICIjQ09WSUQxOSIsIAogIAogICMgU2V0IHRoZSBudW1iZXIgb2YgdHdlZXRzIHRvIHJldHVybgogIG4gPSAxODAwMCwgCiAgCiAgIyBTcGVjaWZ5IHRoZSB0eXBlIG9mIHNlYXJjaCByZXN1bHRzCiAgdHlwZSA9ICJtaXhlZCIsCiAgCiAgIyBEbyBub3QgaW5jbHVkZSByZXR3ZWV0cwogIGluY2x1ZGVfcnRzID0gRkFMU0UsIAogIAogICMgU2V0IHR3ZWV0IGxhbmd1YWdlIHRvIEVuZ2xpc2gKICBsYW5nID0gImVuIiwgCiAgCiAgIyBUd2l0dGVyIHJhdGUgbGltaXRzIGNhcCB0aGUgbnVtYmVyIG9mIHNlYXJjaCByZXN1bHRzIHJldHVybmVkIHRvIDE4LDAwMCBldmVyeSAxNSBtaW51dGVzLiBUbyByZXF1ZXN0IG1vcmUgdGhhbiB0aGF0LCBzaW1wbHkgc2V0IHJldHJ5b25yYXRlbGltaXQgPSBUUlVFIGFuZCBydHdlZXQgd2lsbCB3YWl0IGZvciByYXRlIGxpbWl0IHJlc2V0cy4KICByZXRyeW9ucmF0ZWxpbWl0ID0gVFJVRQopCmBgYAoKYGBge3J9CiMgc2F2ZSB0aGUgcmV0dXJuZWQgZGF0YSB0byBjc3YgZm9yIGZ1dHVyZSB1c2UKc2F2ZV9hc19jc3YocnQsICJjb3ZpZDE5LmNzdiIpCmBgYAoKQWZ0ZXIgc2F2aW5nIHRoZSB0d2VldHMgZGF0YSwgd2UgY2FuIGxvYWQgaXQgZGlyZWN0bHkuCmBgYHtyfQpydCA8LSByZWFkLmNzdihmaWxlID0gImNvdmlkMTkuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCmBgYHtyfQojIGNoZWNrIHRoZSBkaW1lbnNpb24gb2YgcmV0dXJuZWQgdHdlZXRzIGRhdGEKZGltKHJ0KQpgYGAKVGhlcmUgYXJlIDE3OTQ3IHR3ZWV0cyByZXR1cm5lZCB0byBvdXIgZGF0YWZyYW1lIHdpdGggOTAgdmFyaWFibGVzLgoKYGBge3J9CiMgVmlldyB0aGUgZGF0YWZyYW1lClZpZXcocnQpCmBgYAoKQmVjYXVzZSB3ZSBzcGVjaWZpZWQgdGhlIHNlYXJjaCByZXN1bHRzIGJ5IGAibWl4ZWQiYCwgc28gbGV0J3Mgb3JkZXIgdGhlIHR3ZWV0cyBieSBwb3N0IHRpbWUgYW5kIGNoZWNrIHRoZSBmaXJzdCBhbmQgbGFzdCAxMCB0d2VldHMuCmBgYHtyfQojIGNoZWNrIHRoZSBmaXJzdCAxMCByb3dzIGJ5IG9yZGVyaW5nIGNyZWF0ZWRfYXQgdGltZQpoZWFkKHJ0W29yZGVyKHJ0JGNyZWF0ZWRfYXQpLF0sIDEwKQpgYGAKCmBgYHtyfQojIGNoZWNrIHRoZSBsYXN0IDEwIHJvd3MgYnkgb3JkZXJpbmcgY3JlYXRlZF9hdCB0aW1lCnRhaWwocnRbb3JkZXIocnQkY3JlYXRlZF9hdCksXSwgMTApCmBgYApPbmx5IDMgdHdlZXRzIHdlcmUgcG9zdGVkIGF0IGxlYXN0IDEwIGhvdXJzIHRoYW4gb3RoZXJzLiBTbyB3ZSBjYW4gcGxvdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBjcmVhdGVkIGJ5IHRpbWUgdG8gc2VlIHRoZSB0cmVuZC4KCmBgYHtyfQojIFBsb3QgdGhlIGZyZXF1ZW5jeSBvZiB0d2VldHMgYWZ0ZXIgMjAyMC0wMy0yOCAyMzowMDowMCB3aXRoIGludGVydmFsIDEgbWluCnRzX3Bsb3QocnQgJT4lIGZpbHRlcihjcmVhdGVkX2F0ID4gIjIwMjAtMDMtMjggMjM6MDA6MDAiKSwgCiAgICAgICAgYnkgPSAibWlucyIsIAogICAgICAgIHRyaW0gPSAxLCAKICAgICAgICBjb2xvciA9ICIjMDBhY2VlIiAjIHVzZSB0d2l0dGVyIGxvZ28gYmx1ZQogICAgICAgICkgKwogICAgICAgIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKSArCiAgICAgICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkgKwogICAgICAgIGdncGxvdDI6OmxhYnMoCiAgICAgICAgICB4ID0gTlVMTCwgeSA9IE5VTEwsCiAgICAgICAgICB0aXRsZSA9ICJGcmVxdWVuY3kgb2YgI0NPVklEMTkgdHdlZXRzIGZyb20gcGFzdCBob3VycyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJUd2l0dGVycyBjb3VudHMgYWdncmVnYXRlZCB1c2luZyAxIG1pbnMgaW50ZXJ2YWxzIiwKICAgICAgICAgIGNhcHRpb24gPSAiXG5Tb3VyY2U6IERhdGEgY29sbGVjdGVkIGZyb20gVHdpdHRlcidzIFJFU1QgQVBJIHZpYSBydHdlZXQiCiAgICAgICAgICApCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBudW1iZXIgb2YgdHdlZXRzIGNyZWF0ZWQgcGVyIG1pbnV0ZXMgaXMgaW4gNTAgdG8gMTUwIHJhbmdlLiBBdCBldmVyeSBvJ2Nsb2NrIGFuZCBoYWxmIHBhc3QgbydjbG9jaywgdGhlIG51bWJlciBvZiB0d2VldHMgcHVibGlzaGVkIGlzIG11Y2ggaGlnaGVyIHRoYW4gb3RoZXIgdGltaW5nLgoKCiMgMi4gRGF0YSBUcmFuc2Zvcm1hdGlvbgoKV2Ugd2lsbCB1c2UgdGhlIHRleHQgY29sdW1uIG9mIG91ciBgcnRgIGRhdGFzZXQgZm9yIHRleHQgYW5hbHlzaXMgc28gaXQgaXMgbmVlZGVkIHRvIHRyYW5zZm9ybSB0aGUgdGV4dCBjb2x1bW4gdG8gY29ycHVzIHR5cGUuCmBgYHtyfQojIHRyYW5zZm9ybSBvdXIgZGF0YSB0byBzb3VyY2UgZm9ybWF0CmNvdmlkIDwtIFZlY3RvclNvdXJjZShydCR0ZXh0KQoKIyBjaGVjayB0aGUgZmlyc3QgdHdlZXQKY292aWRbMV0KYGBgCgpgYGB7cn0KIyB0cmFuc2Zvcm0gdGhlIHNvdXJjZSBmaWxlIGludG8gYSBDb3JwdXMKY3ZfY29ycHVzIDwtIFZDb3JwdXMoY292aWQpCgojIHRoZW4gaW5zcGVjdCB0aGUgZmlyc3Qgb25lIHR3ZWV0Cmluc3BlY3QoY3ZfY29ycHVzWzFdKQpgYGAKCiMgMy4gRGF0YSBDbGVhbmluZwoKTm93IHRleHQgY2xlYW5pbmcgaXMgdG8gYmUgY29uZHVjdGVkLCBhcyBmb3IgdGV4dCBhbmFseXNpcyBpdCBpcyBuZWNlc3NhcnkgdG8gcmVtb3ZlIHB1bmN0dWF0aW9uLCBudW1iZXJzLCB3aGl0ZSBzcGFjZSwgc3RvcCB3b3JkcyBhbmQgc3BlY2lmaWVkIHdvcmRzIHN1Y2ggYXMgInZpcnVzIiwgImNvdmlkIiwgImNvcm9uYXZpcnVzIi4gVGhlcmVmb3IsIHdlIGNhbiBjcmVhdGUgYSBmdW5jdGlvbiB0byBjbGVhbiBvdXIgdGV4dCBkYXRhLgpgYGB7cn0KY2xlYW5fY29ycHVzIDwtIGZ1bmN0aW9uKGNvcnApIHsKICAKICAjIHJlbW92ZSBhbnkgaHRtbCBmb3JtYXQgaWYgYW55CiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX2h0bWwpKQogIAogICMgcmVtb3ZlIGFueSBVUkxzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX3VybCkpICAKICAKICAjIHRyYW5zZm9ybSBhbGwgY2hhcmFjdGVycyB0byBsb3dlciBjYXNlCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKICAKICAjIHdvcmQgc3RlbW1pbmcKICBjb3JwIDwtIHRtX21hcChjb3JwLCBzdGVtRG9jdW1lbnQpICAKICAKICAjIHJlbW92ZSBwdW5jdHVhdGlvbgogIGNvcnAgPC0gdG1fbWFwKGNvcnAsIHJlbW92ZVB1bmN0dWF0aW9uKQoKICAjIHJlbW92ZSBudW1iZXJzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlTnVtYmVycykgIAogIAogICMgc3RyaXAgZXh0cmEgd2hpdGVzcGFjZQogIGNvcnAgPC0gdG1fbWFwKGNvcnAsIHN0cmlwV2hpdGVzcGFjZSkKICAKICAjIFJlbW92ZSBlbmdsaXNoIGNvbW1vbiBzdG9wd29yZHMKICBjb3JwIDwtIHRtX21hcChjb3JwLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCiAgCiAgIyByZW1vdmUgc3BlY2lmaWVkIHdvcmRzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlV29yZHMsIAogICAgICAgICAgICAgICAgIGMoImNvdmlkMTkiLCAiY29yb25hdmlydXMiLCAiY292aWQiLCAid2lsbCIsICJjYW4iLCAibm93IiwgImdldCIsICJqdXN0IiwKICAgICAgICAgICAgICAgICAgICJuZXciLCAicGVvcGwiLCAib25lIiwgInZpcnVzIiwgImNhc2UiLCAiZGF5IiwgInRpbWUiLCAicGVuZGVtIiwgImxpa2UiLAogICAgICAgICAgICAgICAgICAgInRha2UiLCAibWFrZSIsICJzYXkiLCAic2VlIikpCgogIHJldHVybihjb3JwKQp9CmBgYAoKYGBge3J9CiMgQXBwbHkgdGhlIGZ1bmN0aW9uIHRvIG91ciBjb3JwdXMgZGF0YQpjdl9jb3JwdXNfY2xlYW5lZCA8LSBjbGVhbl9jb3JwdXMoY3ZfY29ycHVzKQpgYGAKCmBgYHtyfQojIGNvbXBhcmUgdGhlIGZpcnN0IHR3ZWV0IGJlZm9yZSBhbmQgYWZ0ZXIgY2xlYW5pbmcKY2F0KCJCZWZvcmU6XG4iLCBjb250ZW50KGN2X2NvcnB1c1tbMV1dKSwgIlxuXG4iKQoKY2F0KCJBZnRlcjpcbiIsIGNvbnRlbnQoY3ZfY29ycHVzX2NsZWFuZWRbWzFdXSkpCmBgYApPdXIgZnVuY3Rpb24gc3VjY2Vzc2Z1bGx5IGNoYW5nZWQgYWxsIGxldHRlcnMgdG8gbG93ZXJjYXNlLCByZW1vdmVkIHRoZSBudW1iZXJzLCB1cmwsIHB1bmN0dWF0aW9uLCBzdG9wIHdvcmRzIGFuZCB0aGUgc3BlY2lmaWVkIHdvcmRzLiBJbiBhZGRpdGlvbiwgdGhlIHN0ZW1taW5nIHdhcyBjb25kdWN0ZWQgZm9yIHdvcmRzIGxpa2UgInJlcG9ydGVkIiwgImNvbmZpcm1lZCIsICJkaXNjaGFyZ2VkIi4gTm93IHdlIGNhbiBzdGFydCBvdXIgbW9kZWxsaW5nIHdvcmsuCgoKIyA0LiBNb2RlbGxpbmcKCkluIG1vZGVsbGluZyBwYXJ0LCB3ZSB3aWxsIGFwcGx5IGZvdXIgbWV0aG9kcyB0byB1bmRlcnN0YW5kIHRoZSBDT1ZJRC0xOSB0d2VldHMgZGF0YS4KMS4gQmFnIG9mIFdvcmRzCjIuIEdhdWdlIFNlbnRpbWVudAozLiBBc3NvY2lhdGlvbiBSdWxlcwo0LiBUb3BpYyBNb2RlbGxpbmcKCiMjIDQuMSBCYWcgb2YgV29yZHMgTW9kZWwKYGBge3J9CiMgdHJhbnNmb3JtIHRoZSBjbGVhbmVkIGRhdGEgaW50byBhIGRvYy10ZXJtIG1hdHJpeApkdF9jdl9jb3JwdXMgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGN2X2NvcnB1c19jbGVhbmVkKQoKIyBwcmludCB0aGUgZGltZW5zaW9uIG9mIHRoZSBtYXRyaXgKZGltKGR0X2N2X2NvcnB1cykKYGBgCldlIGhhdmUgMTc5NTEgdHdlZXRzIGFuZCAzODk2MiBkaWZmZXJlbnQgd29yZHMvdGVybXMuCgpgYGB7cn0KIyB0cmFuc2Zvcm0gdG8gbWF0cml4IGZvcm1hdAptdF9jdl9jb3JwdXMgPC0gYXMubWF0cml4KGR0X2N2X2NvcnB1cykKYGBgCgpgYGB7cn0KIyB2aWV3IGEgcG9ydGlvbiBvZiB0aGUgbWF0cml4Cm10X2N2X2NvcnB1c1sxOjEwLCA1MDo1NV0KYGBgCmBgYHtyfQojIHZpc3VhbGlzZSB0d2l0dGVyIGRhdGEKIyBjb252ZXJ0IGZyb20gbWF0cml4IHRvIHRpZHkgZGF0YQp0aWR5X2RhdGEgPC0gdGlkeShkdF9jdl9jb3JwdXMpCgp0aWR5X2RhdGEKCiMgdGhlIHRpZHlfZGF0YSB3b3VsZCBiZSB1c2VkIGZvciB0aGUgc2VudGltZW50IGFuYWx5c2lzIGluIHRoZSBsYXRlciBzZWN0aW9uLgpgYGAKYGBge3J9CnRlcm1fZnJlIDwtIGNvbFN1bXMobXRfY3ZfY29ycHVzKQpgYGAKCmBgYHtyfQojIHNvcnQgdGVybSBmcmVxdWVuY3kgaW4gZGVjcmVhc2luZyBvcmRlcgp0ZXJtX2ZyZSA8LSBzb3J0KHRlcm1fZnJlLCBkZWNyZWFzaW5nID0gVFJVRSkKYGBgCgpgYGB7cn0KIyBjaGFuZ2UgdGhlIHRlcm1fZnJlIHRvIGRhdGFmcmFtZSB0eXBlCmZyZXFfd29yZCA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh0ZXJtX2ZyZSksIGZyZXEgPSB0ZXJtX2ZyZSkKCiMgdmlldyB0aGUgMjAgbW9zdCBmcmVxdWVudCB0ZXJtcwpoZWFkKGZyZXFfd29yZCwgMjApCmBgYAoKYGBge3J9CiMgcGxvdCB0aGUgbW9zdCBmcmVxdWVudCAyMCB0ZXJtcyBpbiBvdXIgZGF0YQpnZ3Bsb3QoaGVhZChmcmVxX3dvcmQsIDIwKSwgYWVzKHggPSByZW9yZGVyKHdvcmQsIGZyZXEpLCB5ID0gZnJlcSksIGZpbGwgPSAiYmx1ZSIpICsgCiAgZ2VvbV9jb2woZmlsbCA9ICIjMDBhY2VlIikgKwogIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF9ibGFuaygpKSArCiAgZ2dwbG90Mjo6bGFicygKICAgIHggPSBOVUxMLCB5ID0gTlVMTCwKICAgIHRpdGxlID0gIk1vc3QgZnJlcXVlbnQgMjAgd29yZHMgaW4gI0NPVklEMTkgdHdlZXRzXG4iLAogICAgY2FwdGlvbiA9ICJcblNvdXJjZTogRGF0YSBjb2xsZWN0ZWQgZnJvbSBUd2l0dGVyJ3MgUkVTVCBBUEkgdmlhIHJ0d2VldCIKICApICsKICBjb29yZF9mbGlwKCkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZnJlcSksIGhqdXN0ID0gKzEuMiwgY29sb3IgPSAiI0ZGRkZGRiIsIHNpemUgPSAzKQpgYGAKCkhhdmluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdGVybXMsIGxldCdzIG1ha2UgYSB3b3JkY2xvdWQgdG8gdmlzdWFsaXNlIHRoZSBtb3N0IGZyZXF1ZW50IHRlcm1zLgpgYGB7cn0KIyBjcmVhdGUgYSB2ZWN0b3Igb2YgdGVybSBmcmVxdWVuY3kgdmFsdWVzCnRlcm1zX3ZlYyA8LSBuYW1lcyh0ZXJtX2ZyZSkKYGBgCgpgYGB7cn0KIyBjcmVhdGUgd29yZGNsb3VkIGZvciB0aGUgODAgbW9zdCBmcmVxdWVudCB3b3JkcyB3aXRoIG1pbmltYWwgZnJlcXVlbmN5IG92ZXIgMzAwCndvcmRjbG91ZCh3b3JkcyA9IHRlcm1zX3ZlYywKICAgICAgICAgIGZyZXEgPSB0ZXJtX2ZyZSwKICAgICAgICAgIG1pbi5mcmVxID0gMzAwLAogICAgICAgICAgbWF4LndvcmRzID0gODAsCiAgICAgICAgICBzY2FsZSA9IGMoMywwLjEzKSwgIyBzY2FsZSB0aGUgc2l6ZSBvZiBkaWZmZXJlbnQgbGV2ZWwgb2YgZnJlcXVlbmN5CiAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgIyBtb3JlIGZyZXF1ZW50IHdvcmRzIHdpbGwgYmUgaW4gY2VudGVyCiAgICAgICAgICByb3QucGVyID0gMC4wLCAjIHByb3BvcnRpb24gd29yZHMgd2l0aCA5MCBkZWdyZWUgcm90YXRpb24KICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoNiwgIlNldDIiKSkgIyBTZXQgY29sb3IgdGhlbWUgd2l0aCA1IGNvbG9ycwpgYGAKClRoZSB3b3JkbG91ZCBzaG93IHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzIGluIHRoZSBtaWRkbGUuIFdlIGNhbiBzZWUgd29yZHMgc3VjaCBhcyBgbmVlZGAsIGBoZWxwYCwgYGR1cmVgLCBgdGVzdGAsIGBkZWF0aGAsIGBzdGF5YCwgYGhvbWVgLiBUaGVzZSB3b3JkcyBhcmUgZGVmaW5pdGVseSBhc3NvY2lhdGVkIHdpdGggY29yb25hdmlydXMgYXMgd2Uga25vdyB0aGUgc2l0dWF0aW9uIGFyb3VuZCB0aGUgYHdvcmxkYC4gTWFueSBwZW9wbGUgbmVlZCBgaGVscGAgYW5kIGN1cmUgYXMgdGhleSBoYWQgdGFrZW4gYHRlc3RgIGFuZCBpbmNyZWFzaW5nIG51bWJlcnMgb2YgcGVvcGxlIHdlcmUgcG9zaXRpdmUuIE1hbnkgcGVvcGxlIGFyZSBgZHVyZWx5YCBzdWZmZXJpbmcgZnJvbSB0aGUgYHBhbmRlbWljYC4gVGhlIHZpcnVzIGhhcyBsZWFkIHRvIG1vcmUgdGhhbiAxMCB0aG91c2FuZCBgZGVhdGhgLiBUbyBgcHJvdGVjdGAgb3Vyc2VsdmVzLCBpdCBpcyBiZXR0ZXIgdG8gYHN0YXkgaG9tZWAgYW5kIGB3b3JrYCBmcm9tIGhvbWUuCgpBbHRlcm5hdGl2ZSB3YXkgdG8gdmlzdWFsaXNlIHRoZSB3b3JkY2xvdWQgYnkgYHdvcmRjbG91ZDJgIHBhY2thZ2UuIEl0IGNyZWF0ZXMgYSB3aWRnZXQgYW5kIGdlbmVyYXRlIHRoZSB3b3JkcyBmcm9tIG1vc3QgZnJlcXVlbnQgdG8gbGVzcyBmcmVxdWVudC4KYGBge3J9CmxpYnJhcnkod29yZGNsb3VkMikKd29yZGNsb3VkMihkYXRhID0gaGVhZChmcmVxX3dvcmQsIDEwMDApLCBmb250RmFtaWx5ID0gIkhlbHZldGljYSIsIHNoYXBlID0gImRpYW1vbmQiKQpgYGAKCgojIyA0LjIgR2F1Z2UgU2VudGltZW50CgpQcmV2aW91c2x5IHdlIGdvdCBgdGlkeV9kYXRhYCwgbm93IHdlIHVzZSBpdCBmb3Igc2VudGltZW50IGFuYWx5c2lzLgpgYGB7cn0KIyBqb2luIHRoZSB0aWR5IGRhdGEgd2l0aCB0aGUgY29ycmVzcG9uZGluZyBzZW50aW1lbnQKdHdlZXRfc2VudGltZW50IDwtIHRpZHlfZGF0YSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIiksIGJ5ID0gYyh0ZXJtID0gIndvcmQiKSkgIyBzZXQgc2VudGltZW50IGxleGljb24gdG8gYmluZwpgYGAKCkJlY2F1c2Ugd2UgY2hvc2UgYGJpbmdgIGxleGljb24sIGFsbCB0ZXJtcyB3aWxsIGJlIGNhdGVnb3JpemVkIGluIGEgYmluYXJ5IGZhc2hpb24gaW50byBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4KYGBge3J9CiMgaW5zcGVjdCB0aGUgcmVzdWx0CnR3ZWV0X3NlbnRpbWVudApgYGAKClRoZSByZXN1bHQgc2hvd3MgZWFjaCB0ZXJtIGluIGVhY2ggZG9jdW1lbnQgaGFzIGl0cyBzZW50aW1lbnQgY2F0ZWdvcnkgLS0gYHBvc2l0aXZlYCBvciBgbmVnYXRpdmVgLiBUbyB2aXN1YWxpc2UgdGhlIG1vc2UgZnJlcXVlbnQgYHBvc2l0aXZlYCBhbmQgYG5lZ2F0aXZlYCB3b3Jkcywgd2Ugc2hvdWxkIGdyb3VwIG91ciBkYXRhIGJ5IGBzZW50aW1lbnRgIGFuZCBgdGVybWAgd2l0aCBzdW0gdmFsdWUgZm9yIGBjb3VudGAuCgpgYGB7cn0KdHdlZXRfc2VudGltZW50ICU+JQogICMgc3VtIHRoZSBjb3VudCBvZiBlYWNoIHRlcm1zIHBlciBzZW50aW1lbnQKICBjb3VudChzZW50aW1lbnQsIHRlcm0sIHd0ID0gY291bnQpICU+JSAjIGNvdW50IHRoZSBudW1iZXIgb2YgZnJlcXVlbmN5IG9mIHRlcm1zCiAgZmlsdGVyKG4gPiAyMDApICU+JSAjIG9ubHkga2VlcCB0ZXJtcyBvY2N1ciBtb3JlIHRoYW4gMjAwIHRpbWVzCiAgbXV0YXRlKG4gPSBpZmVsc2Uoc2VudGltZW50ID09ICJuZWdhdGl2ZSIsIC1uLCBuKSkgJT4lICMgY2hhbmdlIHRoZSBuIG9mIG5lZ2F0aXZlIHRvIC1uCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyKHRlcm0sIG4pKSAlPiUKICAKICAjIHZpc3VhbGlzYXRpb24KICBnZ3Bsb3QoYWVzKHRlcm0sIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woKSArICMgYmFyIGNoYXJ0CiAgZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpICsgIyBzZXQgcGxvdCB0aGVtZQogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF9ibGFuaygpKSArICMgZGVsZXRlIHggdGljayBsYWJlbHMKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiwgeSA9IG4gLSA1MCpzaWduKG4pKSwgY29sb3IgPSAiI0ZGRkZGRiIsIHNpemUgPSAzKSArICMgYWRkIGRhdGEgbGFiZWwKICBnZ3Bsb3QyOjpsYWJzKAogICAgeCA9IE5VTEwsIHkgPSBOVUxMLCAjIHJlbW92ZSB4LCB5IGF4aXMgbGFiZWxzCiAgICB0aXRsZSA9ICJNb3N0IGZyZXF1ZW50IHdvcmRzIHBlciBzZW50aW1lbnQiLAogICAgY2FwdGlvbiA9ICJcblNvdXJjZTogRGF0YSBjb2xsZWN0ZWQgZnJvbSBUd2l0dGVyJ3MgUkVTVCBBUEkgdmlhIHJ0d2VldCIpICsKICBjb29yZF9mbGlwKCkgIyByZXZlcnNlIHgveSBheGlzCmBgYAoKV2UgY2FuIG1ha2UgYSBjb21wYXJpc29uIHdvcmRjbG91ZCB0byBzaG93IHRoZSBtb3N0IGZyZXF1ZW50IHBvc3RpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzIGF0IHRoZSBzYW1lIHBsb3QuCmBgYHtyfQp0aWR5X2RhdGEgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpLCBieSA9IGModGVybSA9ICJ3b3JkIikpICU+JQogIGNvdW50KHRlcm0sIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGFjYXN0KHRlcm0gfiBzZW50aW1lbnQsIHZhbHVlLnZhciA9ICJuIiwgZmlsbCA9IDApICU+JSAjIGNoYW5nZSBtb2x0ZW4gZnJhbWUgdG8gd2lkZSBmcmFtZQogIHN1YnNldChzZWxlY3QgPSBjKDIsIDEpKSAlPiUgIyBwdXQgcG9zaXRpdmUgYmVmb3JlIG5lZ2F0aXZlIGJ5IHJlb3JkZXIgY29sdW1ucwogIGNvbXBhcmlzb24uY2xvdWQobWF4LndvcmRzID0gMTAwLAogICAgICAgICAgICAgICAgICAgcm90LnBlciA9IDAsCiAgICAgICAgICAgICAgICAgICBtYXRjaC5jb2xvcnMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICkKYGBgCkJ5IGNvbXByaXNvbiB3b3JkY2xvdWQsIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgdHdvIGNhdGVnb3J5IHdvcmRzLiBQb3NpdGl2ZSBoaWdoIGZyZXF1ZW50IHdvcmRzIGluY2x1ZGUgdGhhbmssIHByb3RlY3QsIHNhZmUsIGxvdmUsIHN1cHBvcnQuIE5lZ2F0aXZlIGhpZ2ggZnJlcXVlbnQgd29yZHMgaW5jbHVkZSBkZWF0aCwgZGllLCByaXNrLCBvdXRicmVhaywgY3Jpc2lzLCBzeW1wdG9tLgoKVGhlIGBucmNgIGxleGljb24gY2FuIGNhdG9nZXJpemUgd29yZHMgd2l0aCAxMCBzZW50aW1lbnRzLgpgYGB7cn0KIyBsaXN0IGFsbCBzZW50aW1lbnQgY2F0ZWdvcmllcyBieSBucmMgdHlwZQp1bmlxdWUoZ2V0X3NlbnRpbWVudHMoIm5yYyIpJHNlbnRpbWVudCkKYGBgCgpTbyB3ZSBjYW4gbWFrZSBhIGNvbXBhcmlzb24gd29yZGNsb3VkIGZvciB0aGUgb3RoZXIgOCBzZW50aW1lbnRzIGV4Y2x1ZGluZyBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4KYGBge3J9CmRhdGFfdGlkeSA8LSB0aWR5X2RhdGEgJT4lCiAgIyBJbm5lciBqb2luIHRvIG5yYyBsZXhpY29uCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIiksIGJ5ID0gYygidGVybSIgPSAid29yZCIpKSAlPiUgCiAgCiAgIyBEcm9wIHBvc2l0aXZlIG9yIG5lZ2F0aXZlCiAgZmlsdGVyKCFncmVwbCgicG9zaXRpdmV8bmVnYXRpdmUiLCBzZW50aW1lbnQpKSAlPiUgCiAgCiAgIyBDb3VudCBieSBzZW50aW1lbnQgYW5kIHRlcm0KICBjb3VudChzZW50aW1lbnQsIHRlcm0pICU+JSAKICAKICAjIFNwcmVhZCBzZW50aW1lbnQsIHVzaW5nIG4gZm9yIHZhbHVlcwogIHNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPSAwKSAgJT4lIAogIAogICMgQ29udmVydCB0byBkYXRhLmZyYW1lLCBtYWtpbmcgdGVybSB0aGUgcm93IG5hbWVzCiAgZGF0YS5mcmFtZShyb3cubmFtZXMgPSAidGVybSIpCgojIFBsb3QgY29tcGFyaXNvbiBjbG91ZApjb21wYXJpc29uLmNsb3VkKGRhdGFfdGlkeSwgCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gMTAwLCAKICAgICAgICAgICAgICAgICB0aXRsZS5zaXplID0gMSwKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgIyBtb3JlIGZyZXF1ZW50IHdvcmRzIHdpbGwgYmUgaW4gY2VudGVyCiAgICAgICAgICAgICAgICAgcm90LnBlciA9IDAuMCwgIyBwcm9wb3J0aW9uIHdvcmRzIHdpdGggOTAgZGVncmVlIHJvdGF0aW9uCiAgICAgICAgICAgICAgICAgY29sb3JzID0gYnJld2VyLnBhbCg4LCAiU2V0MiIpICMgU2V0IGNvbG9yIHRoZW1lIHdpdGggOCBjb2xvcnMgZm9yIDggc2VudGltZW50cwogICAgICAgICAgICAgICAgICkKYGBgCgpNb3N0IHdvcmRzIHVuZGVyIGVhY2ggc2VudGltZW50IGFyZSBjb3JyZWN0bHkgYXNzb2NpYXRlZCB3aXQgdGhlIHNlbnRpbWVudC4gSXQncyBhIHN1cnByaXNlIHRoYXQKdGhlIHdvcmQgYHRydW1wYCBiZWxvbmcgdG8gYHN1cnByaXNlYCBzZW50aW1lbnQuIE1heWJlIGl0IGlzIG5vdCBhIHN1cnByaXNlIGFzIGhlIGFsd2F5cyBnaXZlIHVzIHN1cnByaXNlIDopLgoKCiMjIDQuMyBBc3NvY2lhdGlvbiBSdWxlcwoKQnkgYXNzb2ljaWF0aW9uIHJ1bGVzIGFuYWx5c2lzLCB3ZSBjYW4gZmluZCB0aGUgd29yZHMgYXNzb2NpYXRlZCB3aXRoIHNwZWNpZmljIHdvcmQuIEZpcnN0bHkgd2UgbmVlZCB0byByZW1vdmUgdGhlIHRlcm1zIGhhdmluZyBoaWdoIHNwYXJzaXR5ICgwLjk5OSkuCmBgYHtyfQojIHJlbW92ZSB0aGUgdGVybXMgaGF2aW5nIDAuOTk5IHNwYXJzaXR5LCBvbmx5IHRlcm1zIG9jY3VyaW5nIGluIDAuMSUgZG9jdW1lbnRzIGFyZSByZXRhaW5lZApuZXdfZG9jX3Rlcm0gPC0gcmVtb3ZlU3BhcnNlVGVybXMoZHRfY3ZfY29ycHVzLCAwLjk5KQoKbmV3X2RvY190ZXJtCgojIENvbnZlcnQgdG8gbWF0cml4Cm5ld19tYXQgPC0gYXMubWF0cml4KG5ld19kb2NfdGVybSkKYGBgCgoxOTcgdGVybXMgZnVsZmlsbCBvdXIgZmlsdGVyaW5nIHJlcXVpcmVtZW50LiBOb3cgd2UgY2FuIGNoZWNrIHRoZSBhc3NvY2lhdGVkIHdvcmRzLiBBcyB0aGUgdGVybSBgbmVlZGAgaXMgdGhlIG1vc3QgZnJlcXVlbnQgd29yZCwgbGV0J3Mgc2VlIHdoYXQgcGVvcGxlIGBuZWVkYCBtb3N0LgpgYGB7cn0KIyBVc2UgdGhlIGZpbmRBc3NvY3MgZnVuY3Rpb24gdG8gZmluZCB3b3JkcyBhc3NvY2lhdGVkIHdpdGggdGhlICduZWVkJwpmaW5kQXNzb2NzKG5ld19kb2NfdGVybSwgIm5lZWQiLCAwLjAzKQpgYGAKCkl0IGlzIG5vdCBzdXByaXNlZCB0byBzZWUgdGhhdCBwZW9wbGUgYG5lZWRgIGBoZWxwYCwgYG5lZWRgIHRvIHRha2UgYGNhcmVgIGFuZCBgcHJvdGVjdGAgdGhlbXNlbHZlcyBmcm9tIHRoZSB2aXJ1cywgYG5lZWRgIHRvIGBrZWVwYCBgc2FmZWAgYW5kIGBuZWVkYCBgcHBlYC4gRm9yIHRob3NlIGluZmVjdGVkLCB0aGV5IGBuZWVkYCBgbnVyc2VgIHRvIGBoZWxwYC4KCk5vdyB3ZSBjYW4gY2hlY2sgd2hhdCB0ZXJtcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGBzYWZlYCwgYXMgaXQgaXMgaW1wb3J0YW50IGZvciBldmVyeW9uZSB0byBrZWVwIHNhZmUgdW5kZXIgQ09WSUQtMTkgb3V0YnJlYWsuCmBgYHtyfQojIFVzZSB0aGUgZmluZEFzc29jcyBmdW5jdGlvbiB0byBmaW5kIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCB0aGUgJ3NhZmUnCmZpbmRBc3NvY3MobmV3X2RvY190ZXJtLCAic2FmZSIsIDAuMDMpCmBgYApUaGUgbW9zdCBhc3NvY2lhdGVkIHdvcmRzIHdpdGggYHNhZmVgIGFyZSBgc3RheWAsIGBrZWVwYCBhbmQgYGhvbWVgLiBBY3R1YWxseSwgYGtlZXBpbmcgc3RheSBob21lYCBpcyB0aGUgbW9zdCBlZmZpY2llbnQgd2F5IHRvIHByb3RlY3Qgb3Vyc2VsdmVzIGZyb20gdGhlIHZpcnVzLiBUaGVyZWZvcmUsIGZyb20gQ09WSUQtMTkgdHdlZXRzIGRhdGEsIEkgdGhpbmsgd2Ugc2hvdWxkIGtub3cgdGhhdCBpdCBpcyBpbXBvcnRhbnQgdG8gc3RheSBob21lIHVuZGVyIGN1cnJlbnQgc2VyaW91cyBzaXR1YXRpb24uCgojIyA0LjUgVG9waWMgTW9kZWxsaW5nCmBgYHtyfQojIHN1bSB0aGUgd29yZHMgaW4gZWFjaCBkb2N1bWVudApzdW1fcm93cyA8LSBhcHBseShuZXdfZG9jX3Rlcm0sIDEsIHN1bSkKCiMgcmVtb3ZlIGRvY3VtZW50cyB3aXRob3V0IHdvcmRzCmR0bV9ub256ZXJvcyA8LSBuZXdfZG9jX3Rlcm1bc3VtX3Jvd3MgPiAwLCBdCmBgYAoKYGBge3J9CiMgYXBwbHkgTERBIGZ1bmN0aW9uIGFuZCBzZXQgdGhlIG51bWJlciBvZiB0b3BpY3MgdG8gNApsZGEgPC0gTERBKGR0bV9ub256ZXJvcywgayA9IDQpCmBgYAoKYGBge3J9CiMgZ2V0IHRoZSBmaXJzdCAxMCB0ZXJtcyBvZiBlYWNoIHRvcGljCnRlcm0gPC0gdGVybXMobGRhLCAxMCkKCnRlcm0KYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBmaXJzdCAxMCB0ZXJtcyBvZiBlYWNoIHRvcGljLgpgYGB7cn0KIyBjb25zdHJ1Y3QgYSB0aWR5IGRhdGEgZnJhbWUgb2YgdGhlIExEQSBtb2RlbCByZXN1bHQgYnkgdGlkeSBmdW5jdGlvbiBmcm9tIHRpZHl0ZXh0IHBhY2thZ2UKdG9waWNzIDwtIHRpZHkobGRhLCBtYXRyaXggPSAiYmV0YSIpCgojIGdlbmVyYXRlIHRoZSB0b3AgMTAgdGVybXMgb2YgZWFjaCB0b3BpYwp0b3BfdGVybXMgPC0gdG9waWNzICU+JQogIGdyb3VwX2J5KHRvcGljKSAlPiUKICB0b3BfbigxMCwgYmV0YSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKIyBwbG90IHRoZSBmaXJzdCAxMCB0ZXJtcyBieSBiZXRhIHZhbHVlCnRvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lCiAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZ3Bsb3QyOjpsYWJzKAogICAgeCA9IE5VTEwsCiAgICB0aXRsZSA9ICJUb3AgMTAgdGVybXMgaW4gZWFjaCB0b3BpYyIsCiAgICBjYXB0aW9uID0gIlxuU291cmNlOiBEYXRhIGNvbGxlY3RlZCBmcm9tIFR3aXR0ZXIncyBSRVNUIEFQSSB2aWEgcnR3ZWV0IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9yZW9yZGVyZWQoKQpgYGAKVGhlIGJldGEgdmFsdWUgdGVsbHMgdXMgcHJvYmFiaWxpdHkgb2YgdGhhdCB0ZXJtICh3b3JkKSBiZWxvbmdpbmcgdG8gdGhhdCB0b3BpYy4KCldlIGNhbiBzZWUgc29tZSB0ZXJtcyBsaWtlIGBoZWxwYCwgYG5lZWRgLCBgdHJ1bXBgIGFuZCBgcGFuZGVtaWNgIGFwcGVhciBpbiBtb3JlIHRoYW4gMSB0b3BpYywgdGhpcyBpcyBub3Qgc3VycHJpc2VkLiBUaGVyZSBhcmUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGVzZSBjb2xsZWN0aW9ucyBvZiB0ZXJtcy4gCi0gVG9waWMgMSBpcyBhYm91dCBgdGVzdGAsIGBkaWVgIGZyb20gYHBvc2l0aXZlYCwgbGlrZSBhIHRvcGljIGFib3V0IHRoZSBuZXcgY2FzZXMgYW5kIGRlYXRoIHJlcG9ydC4gCi0gVG9waWMgMiBpcyBhYm91dCBgc3RheSBob21lYCwgYG5lZWRgIGFuZCBgaGVscGAsIGxpa2UgYSB0b3BpYyBhYm91dCB3aGF0IHBlb3BsZSBkby4KLSBUb3BpYyAzIGlzIG1vcmUgcmVsYXRlZCB0byBgY2hpbmFgLiBXZSBrbm93IHRoYXQgcmVjZW50bHkgdGhlIHNwcmVhZGluZyBwYWNlIG9mIHRoZSBwYW5kZW1pYyBpbiBDaGluYSBpcyBhbG1vc3Qgc3RvcHBlZC4gCi0gSW4gdG9waWMgNCwgdGhlIHRlcm1zIGB0cnVtcGAsIGBrbm93YCBhbmQgYGdvb2RgIGFwcGVhciB0b2dldGhlci4KCiMgNS4gQ29uY2x1c2lvbgpCeSB1c2luZyBhIGNvbWJpbmF0aW9uIG9mIEJhZyBvZiBXb3JkcyBhbmFseXNpcywgc2VudGltZW50IGFuYWx5c2lzLCBhc3NvY2lhdGlvbiBydWxlcyBhbmQgdG9waWMgbW9kZWxpbmcsIHdlIGhhdmUgY29tZSB0byBhIGdvb2QgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGF0ZXN0IENPVklELTE5IHR3ZWV0cy4KCi0gT3ZlcmFsbCwgdGhlIHR3ZWV0cyBjb252ZXkgYSBzaXR1YXRpb24gdGhhdCBtYW55IHBlb3BsZSBuZWVkIGhlbHAgYXMgdGhlIGhpZ2ggZnJlcXVlbmN5IG9mIHdvcmRzIHN1Y2ggYXMgYG5lZWRgLCBgaGVscGAgYW5kIG1hbnkgdHdlZXRzIG1lbnRpb25lZCB3ZSBzaG91bGQgYHN0YXkgaG9tZWAgdG8gYHByb3RlY3RgIG91cnNlbHZlcyBhcyBpdCBpcyByZWFsbHkgbmVjZXNzYXJ5LgotIEJ5IHNlbnRpbWVudCBhbmFseXNpcywgd2Uga25vdyB0aGF0IHRoZSBDT1ZJRC0xOSBoYXMgbGVhZCB0byBhIGxvdCBvZiBkZWF0aCwgYnV0IG1lZGljYWwgZm9yY2VzIGtlZXAgd29ya2luZywgcGVvcGxlIGhlbHAgYW5kIHN1cHBvcnQgZWFjaCBvdGhlciwgdGhpcyBpcyB2ZXJpZmllZCBhcyBieSBuZXdzIHdlIGtub3cgbWFueSBzdG9yaWVzIGFib3V0IHRoZSBoZWxwIGFuZCBsb3ZlIGluIG91ciBmaWdodCB3aXRoIENPVklELTE5LgoKClBTOiBJbiBhZGRpdGlvbiwgd2UgZmluZCB0aGF0IGB0cnVtcGAgaXMgYSAnc3VycHJpc2UnIHRlcm0gYW5kIHRoZSB0ZXJtcyBgdHJ1bXBgLCBga25vd2AgYW5kIGBnb29kYCBhcmUgaW4gc2FtZSB0b3BpYyBieSBvdXIgbW9kZWxsaW5nLiBJIHRoaW5rIHRoaXMgaXMgYSBnb29kIG1vZGVsbGluZywgYXMgJ2FjdHVhbGx5JyBubyBib2R5IGtub3dzIGNvcm9uYXZpcnVzIGJldHRlciB0aGFuIGhlIGRvZXMsIGp1c3QgbGlrZSBpbiBiZWxvdyB2aWRlby4gOikKCmBgYHtyfQpsaWJyYXJ5KCJodG1sdG9vbHMiKQpsaWJyYXJ5KCJ2ZW1iZWRyIikKYGBgCgpgYGB7cn0KZW1iZWRfdXJsKCJodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PXNSM2Y5NUJHSWlBIikKYGBgCg==